<template>
  <div class="css-editor">
    <div class="_controls">
      <div @click="onClickAction('html-png')">生成图片</div>
    </div>
    <div id="_top" class="_top">
      <div v-html="editingCss"></div>
      <div id="editor-preview" v-html="editingHtml"></div>
    </div>
    <div class="_bottom">
      <div ref="editorHtml"></div>
      <div ref="editorCss"></div>
    </div>
  </div>
</template>
<script lang="ts">
import { Vue, Component, Prop, Watch } from 'vue-property-decorator';
import codemirror, { Editor } from 'codemirror';
import 'codemirror/mode/css/css.js';
import 'codemirror/addon/selection/active-line.js';
import 'codemirror/mode/xml/xml.js';
import 'codemirror/addon/edit/closetag.js';
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/paraiso-light.css';
import { Getter, Mutation } from 'vuex-class';
import { renderCssAfter1000ms } from '@/utils';
import html2canvas from 'html2canvas';
import {
  apiCssItemGet,
  apiCssItemAdd,
  apiCssItemUpdate,
  apiCssUpload,
} from '@/http/api/css';

@Component
export default class CssEditor extends Vue {
  private editingHtml: string = '';
  private editingCss: string = '';
  private timerHandle!: NodeJS.Timeout;
  private component!: Vue;
  private originItem: any = {};
  @Prop({ default: '' })
  private uuid!: string;
  @Mutation('postCssItem', { namespace: 'css' })
  private postCssItem!: (data: {
    uuid: string;
    css: string;
    html: string;
    pic: string;
  }) => {};
  @Getter('getCssItem', { namespace: 'css' })
  private getCssItem!: (str: string) => any;

  get editor(): { css: Editor; html: Editor } {
    return {
      css: this.initEditorCss(),
      html: this.initEditorHtml(),
    };
  }

  @Watch('uuid')
  private watchUUID(to: string, from: string) {
    if (from) {
      this.$log.info('info', from, this.editor);
      const css = this.editor.css.getValue();
      const html = this.editor.html.getValue();
      const pic = 'xxx';
      if (css || html) {
        apiCssItemUpdate({
          data: {
            id: from,
            css,
            html,
            pic,
            label: this.originItem.label,
          },
        });
      }
    }
    apiCssItemGet({
      params: {
        id: to,
      },
    }).then(res => {
      if (res.data.success) {
        this.originItem = res.data.data;
        const { css = '', html = '' } = res.data.data;
        this.editor.css.setValue(css);
        this.editor.html.setValue(html);
      }
    });
  }

  private mounted() {
    // this.updateComponent(
    //   '<div @click="clickAction">click{{number}}</div>',
    //   '() => ({ methods: {clickAction() { return this.number++}}, data: () => {return { number: 1}} })',
    // );
  }
  private onClickAction(type: string) {
    switch (type) {
      case 'html-png':
        html2canvas(document.getElementById(
          'editor-preview',
        ) as HTMLElement).then(canvas => {
          canvas.toBlob(
            blob => {
              const formdata = new FormData();
              formdata.append('file', blob || '', 'file.png');
              apiCssUpload({
                data: formdata,
              }).then(res => {
                if (res.data.success) {
                  const css = this.editor.css.getValue();
                  const html = this.editor.html.getValue();
                  const pic = res.data.data.file;
                  if (css || html) {
                    apiCssItemUpdate({
                      data: {
                        id: this.uuid,
                        css,
                        html,
                        pic,
                        label: this.originItem.label,
                      },
                    });
                  }
                }
              });
            },
            'image/png',
            0.8,
          );
        });
        break;
    }
  }
  private initEditorHtml(): Editor {
    this.$log.warn('xxhtml');
    const ins = codemirror(this.$refs.editorHtml as HTMLElement, {
      mode: 'text/html',
      tabSize: 2,
      styleActiveLine: true,
      autoCloseTags: true,
      lineNumbers: true,
    });
    ins.on('change', editor => {
      this.editingHtml = editor.getValue();
    });
    return ins;
  }
  private initEditorCss(): Editor {
    this.$log.warn('xxcss');
    const ins = codemirror(this.$refs.editorCss as HTMLElement, {
      mode: 'text/x-less',
      tabSize: 2,
      styleActiveLine: true,
      lineNumbers: true,
      theme: 'paraiso-light',
    });
    ins.on('change', editor => {
      renderCssAfter1000ms(editor.getValue(), '#editor-preview').then(str => {
        if (str) {
          this.editingCss = str;
        }
      });
    });
    return ins;
  }
  private renderCss(cssStr: string): Promise<string> {
    const pRender = (str: string) => {
      str = `#editor-preview{${str}}`;
      return less.render(str).then(
        output => {
          return Promise.resolve(`<style>${output.css}</style>`);
        },
        e => {
          this.$log.warn(e);
          return Promise.resolve('');
        },
      );
    };
    return new Promise((resolve, reject) => {
      if (this.timerHandle) {
        clearTimeout(this.timerHandle);
      }
      this.timerHandle = setTimeout(() => {
        resolve(true);
      }, 1000);
    }).then(
      needRender => {
        if (needRender) {
          return pRender(cssStr);
        } else {
          return Promise.resolve('');
        }
      },
      () => {
        return Promise.resolve('');
      },
    );
  }

  private updateComponent(html: string, javascript: string) {
    if (this.component) {
      this.component.$destroy();
    }
    const e = eval;
    this.component = new Vue({
      el: '#editor-preview',
      template: html,
      ...e(javascript)(),
    });
  }
}
</script>
<style lang="less">
.css-editor {
  height: 100%;
  display: flex;
  flex-direction: column;
  position: relative;
  > ._controls {
    position: absolute;
    right: 0;
    top: 0;
    padding: 10px;
    z-index: 9;
    > div {
      background-color: #efefef;
      color: #999;
      padding: 8px;
      font-size: 13px;
      cursor: pointer;
      &:hover {
        color: #888;
        box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.5);
      }
    }
  }
  > div._top {
    flex: 1 1 55%;
    padding: 10px;
    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.5);
    overflow: auto;
    > #editor-preview {
      position: relative;
    }
  }
  > div._bottom {
    margin-top: 5px;
    flex: 1 1 45%;
    overflow: auto;
    display: flex;
    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.5);
    padding: 2px;
    > div {
      &:first-child {
        flex: 0 0 55%;
      }
      &:nth-child(2) {
        flex: 0 0 45%;
      }
      overflow: hidden;
      > div {
        height: 100%;
      }
    }
  }
}
</style>